// 接口的作用就是为结构定义契约
// 示例1
interface LabeledValue {
  label: string
}
function printLabel(labelObject: LabeledValue) {
  console.log(labelObject.label)
}

// let myObj = { size: 10, label: 123 } // 错误
let myObj = { size: 10, label: 'Size 10 Object' }
printLabel(myObj)


// 可选属性
interface SquareConfig {
  color?: string;
  width?: number;
  // 字符串索引，意思是 SquareConfig 可能带有上面定义的 color 和 width 属性，并且还会带有任意数量的其他属性
  // [propName: string]: any;
}
function createSquare(config: SquareConfig): { color: string, area: number } {
  console.log('createSquare: ', config)
  let newSquare = { color: "white", area: 100 }
  if (config.color) {
    newSquare.color = config.color
  }
  if (config.width) {
    newSquare.area = config.width * config.width
  }
  return newSquare;
}
// 第一种方式, 使用类型断言, 绕开检查（对象字面量存在任何“目标类型”不包含的属性）
createSquare({width: 123, colur: ''} as SquareConfig)
// 第二种方式, 字符串索引 [propName: string]: any


// 只读属性
interface Point {
  readonly x: number;
  readonly y: number;
}
let p1: Point = { x: 1, y: 2 };
// 尝试修改只读属性 x 时，报错：Cannot assign to 'x' because it is a read-only property.
// p1.x = 10;

// ReadonlyArray<T> 类型
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
// ro[0] = 12;
// ro.push(123);
// ro.length = 100;
// The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
// a = ro;
// 使用类型断言可以跳过限制
a = ro as number[];
a.push(123)
console.log(a)
console.log(ro)




// 接口描述函数类型
interface SearchFunc {
  (source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
  return src.search(sub) > -1
}
// 也可以不写类型，这种情况，参数类型和函数返回值类型由ts类型系统推断出来
// mySearch = function(src, sub) {
//   return src.search(sub) > -1
// }



// 可索引类型
interface StringArray {
  // 索引签名，表示了当用 number 去索引 StringArray 时会得到 string 类型的返回值
  [index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0]
console.log('可索引类型：StringArray: ', myStr)

// interface numberDictionary {
//   [index: string]: number;
//   length: number;
//   // Property 'name' of type 'string' is not assignable to string index type 'number'
//   name: string;
// }

// // 可将索引签名设置为只读的，就可防止给索引赋值
// interface ReadonlyStringArray {
//   readonly [index: number]: string;
// }
// let roMyArray: ReadonlyStringArray = ["Bob", "Mike"];
// // Index signature in type 'ReadonlyStringArray' only permits reading.
// roMyArray[2] = "123";


// 类类型
// 接口描述了类的公共部分，而不是公共和私有两部分；它不会检查类是否具有某些私有成员
// interface ClockInterface {
//   currentTime: Date;
//   setTime(d: Date);
// }
// class Clock implements ClockInterface {
//   currentTime: Date;
//   setTime(d: Date) {
//     this.currentTime = d;
//   }
//   constructor(x: number, y: number) {}
// }


// 检查类的构造函数
interface ClockConstructor {
  new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
  tick();
}
function createClock(ctor: ClockConstructor, hour: number, minute: number) {
  return new ctor(hour, minute)
}
class DigitalClock implements ClockInterface {
  constructor(hour: number, minute: number) {}
  tick() {
    console.log('Digital tick')
  }
}
class AnalogClock implements ClockInterface {
  constructor(hour: number, minute: number) {}
  tick() {
    console.log('Analog tick')
  }
}
let digital = createClock(DigitalClock, 12, 17)
let analog = createClock(AnalogClock, 7, 32)
